package com.longe.security;

import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.AccessControlFilter;
import org.apache.shiro.web.util.WebUtils;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

/**
 * 自定义过滤器
 *
 * 执行过滤器会调用isAccessAllowed方法和onAccessDenied方法，只要两者有一个可以就可以了。
 * 从名字中我们也可以理解，他的逻辑是这样：先调用isAccessAllowed，即是否允许访问，返回 true 表示允许则直接放行
 * 如果返回的是false，则继续执行后面的onAccessDenied方法，如果后面返回的是true则继续执行后面的过滤器
 * 表示访问拒绝时是否自己处理，如果返回 true 表示自己不处理且继续拦截器链执行，返回 false 表示自己已经处理了（比如重定向到另一个页面）。
 */
@Slf4j
public class MyPermissionFilter extends AccessControlFilter {

    /**
     * 表示是否允许访问 ，如果允许访问返回true，否则false；
     * @param request
     * @param response
     * @param o 表示写在拦截器中括号里面的字符串 mappedValue 就是 [urls] 配置中拦截器参数部分
     * @return
     * @throws Exception
     */
    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object o) throws Exception {

        /**如果是登录则直接放行**/
        if(isLoginRequest(request, response)){
            return true;
        }
        return false;
    }

    /**
     * onAccessDenied：表示当访问拒绝时是否已经处理了；如果返回 true表示自己不处理,继续执行下面的过滤链；如果返回 false 表示该拦截器实例已经处理了，将直接返回。
     * @param request
     * @param response
     * @return
     * @throws Exception
     */
    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {

        log.debug("当 isAccessAllowed 返回 false 的时候，才会执行 method onAccessDenied ");
        Subject currentUser = getSubject(request, response);
        if(!currentUser.isAuthenticated()){//未认证状态,跳转至登录页面
            WebUtils.issueRedirect(request, response, getLoginUrl());
            return false;
        }

        String url = getPathWithinApplication(request);
        log.debug("当前用户正在访问的 url => " + url);

        if(!currentUser.isPermitted(url)){
            WebUtils.issueRedirect(request, response, "/unauthorized");
            return false;
        }

        /**返回 false 表示已经处理，例如页面跳转啥的，表示不在走后面的拦截器了（如果还有配置的话）**/
        return true;
    }
}
